/*
 * @(#)XfvhdlTestBenchFile.java        3.0                   2004/09/14
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfvhdl;

import xfuzzy.lang.*;

/**
* Clase que genera el fichero VHDL con el testbench para chequear el 
* correcto funcionamiento del controlador difuso.
* @author Jos� Mar�a �vila Maireles, <b>e-mail</b>: josavimai@alum.us.es
* @version 3.0
*/
public class XfvhdlTestBenchFile {

   //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
   //			  M�TO_DOS P�BLICOS DE LA CLASE				        
   //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

   /**
   * M�todo que crea la cadena que ser� escrita en el fichero de 
   * testbench.
   * @return Devuelve la cadena que ser� escrita en el fichero de 
   * testbench.
   */
   public String createTestBenchSource(Specification spec) {

      XfvhdlHeadFile head =
         new XfvhdlHeadFile(
            XfvhdlProperties.fileDir,
            XfvhdlProperties.outputFile + "TestBench.vhdl",
            XfvhdlProperties.ficheroXFL);

      String code = head.getHead();

      code
         += "\n--***********************************************************************--\n"
         + "--                                                                       --\n"
         + "--   DESCRIPTION: This file contains the VHDL description for the        --\n"
         + "--                testbench of the fuzzy controller.                     --\n"
         + "--                                                                       --\n"
         + "---------------------------------------------------------------------------\n"
         + "--                                                                       --\n"
         + "--   AUTHOR:      Jose Maria Avila Maireles                              --\n"
         + "--                                                                       --\n"
         + "--   VERSION:     Xfvhdl  ver0.1                            April 2004   --\n"
         + "--                                                                       --\n"
         + "--***********************************************************************--\n"
         + "\n"
         + "\n"
         + "library IEEE;\n"
         + "use IEEE.std_logic_1164.all;\n"
         + "use IEEE.std_logic_arith.all;\n"
         + "use IEEE.std_logic_unsigned.all;\n"
         + "use IEEE.std_logic_textio.all;\n"
         + "use std.textio.all;\n"
         + "\n"
         + "use WORK.Constants.all;\n"
         + "\n"
         + "\n"
         + "---------------------------------------------------------------------------\n"
         + "--                           Entity description                          --\n"
         + "---------------------------------------------------------------------------\n"
         + "\n"
         + "entity TestBench is\n"
         + "\n"
         + "\tport(\tclk\t\t: out std_logic;\t\t\t\t\t-- Clock signal.\n"
         + "\t\treset\t\t: out std_logic;\t\t\t\t\t-- Reset signal.\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\t\tin"
            + i
            + "\t\t: out std_logic_vector(N downto 1);\t\t-- Input "
            + i
            + " signal.\n";
      }

      code += "\t\toutput\t: out std_logic_vector(N downto 1);"
         + "\t\t-- Output signal.\n"
         + "\t\tvalid_out\t: out std_logic;"
         + "\t\t\t\t\t-- Valid output signal.\n"
         + "\t\tvalid_in\t: out std_logic);"
         + "\t\t\t\t\t-- Valid input signal.\n"
         + "\n"
         + "end TestBench;\n"
         + "\n"
         + "\n"
         + "---------------------------------------------------------------------------\n"
         + "--                       Architecture description                        --\n"
         + "---------------------------------------------------------------------------\n"
         + "\n"
         + "architecture FPGA of TestBench is\n\n";

      code += "\tsignal clk_signal\t\t: std_logic;\n"
         + "\tsignal reset_signal\t: std_logic;\n"
         + "\tsignal inputs_signal\t: "
         + "std_logic_vector((N * entradas) downto 1);\n"
         + "\tsignal valid_in_signal\t: std_logic;\n"
         + "\tsignal valid_out_signal\t: std_logic;\n"
         + "\tsignal output_signal\t: std_logic_vector(N downto 1);\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\tsignal input"
            + i
            + "_signal\t: std_logic_vector(N downto 1);\n";
      }

      code += "\n\tcomponent FLC\n"
         + "\t\tport(\tclk\t\t: in std_logic;\n"
         + "\t\t\treset\t\t: in std_logic;\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\t\t\tin"
            + i
            + "\t\t: in std_logic_vector(N downto 1);\n";
      }

      code += "\t\t\toutput\t: out std_logic_vector(N downto 1);\n"
         + "\t\t\tvalid_out\t: out std_logic;\n"
         + "\t\t\tvalid_in\t: out std_logic);\n"
         + "\t end component;\n"
         + "\n"
         + "\tfor all: FLC use entity WORK.FLC(FPGA);\n"
         + "\nbegin\n\n"
         + "\t-- Signals assignment.\n"
         + "\tclk\t\t\t<= clk_signal;\n"
         + "\treset\t\t\t<= reset_signal;\n"
         + "\tvalid_in\t\t<= valid_in_signal;\n"
         + "\tvalid_out\t\t<= valid_out_signal;\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\tinput"
            + i
            + "_signal\t<= inputs_signal(("
            + i
            + " * N) downto (("
            + (i - 1)
            + " * N) +1));\n";
      }

      code += "\toutput\t\t<= output_signal;\n\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\tin"
            + i
            + "\t\t\t<= inputs_signal(("
            + i
            + " * N) downto (("
            + (i - 1)
            + " * N) +1));\n";
      }

      // Instancia del controlador difuso
      code += "\n\n"
         + "\tControlador: FLC\t\t\t"
         + "-- Fuzzy logic controller instance.\n"
         + "\t\tport map(\tclk\t\t=> clk_signal,\n"
         + "\t\t\t\treset\t\t=> reset_signal,\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\t\t\t\tin"
            + i
            + "\t\t=> inputs_signal(("
            + i
            + " * N) downto (("
            + (i - 1)
            + " * N) +1)),\n";
      }

      code += "\t\t\t\toutput\t=> output_signal,\n"
         + "\t\t\t\tvalid_out\t=> valid_out_signal,\n"
         + "\t\t\t\tvalid_in\t=> valid_in_signal);\n\n";

      // Proceso que genera la se�al de reloj
      code += "\tClock: process\t\t\t-- Clock signal generator.\n"
         + "\t\tbegin\n"
         + "\t\t\tclk_signal\t<= '0';\n"
         + "\t\t\twait for "
         + (XfvhdlProperties.PERIOD - XfvhdlProperties.EDGE)
         + " ns;\n"
         + "\t\t\tclk_signal <= '1';\n"
         + "\t\t\twait for "
         + XfvhdlProperties.EDGE
         + " ns;\n"
         + "\t\tend process;\n\n";

      // Proceso que genera la se�al de reloj
      code += "\tTest: process\t\t\t-- Testbench process.\n"
         + "\t\tfile fichero\t\t: text is out \"SIM.out\";\n";

      if (XfvhdlProperties.calcArithmetic == false) {
         for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
            code += "\t\tvariable valor1_in"
               + i
               + "\t: integer := 0;\n"
               + "\t\tvariable valor2_in"
               + i
               + "\t: integer := 0;\n"
               + "\t\tvariable valor3_in"
               + i
               + "\t: integer := 0;\n";
         }
      } else {
         for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
            code += "\t\tvariable valor1_in"
               + i
               + "\t: integer := 0;\n"
               + "\t\tvariable valor2_in"
               + i
               + "\t: integer := 0;\n"
               + "\t\tvariable valor3_in"
               + i
               + "\t: integer := 0;\n";
         }
      }

      code += "\t\tvariable valor1_out\t: integer := 0;\n"
         + "\t\tvariable valor2_out\t: integer := 0;\n";

      code += "\t\tvariable ciclos_off\t: integer := 0;\n"
         + "\t\tvariable linea\t\t: line;\n"
         + "\t\tbegin\n"
         + "\t\t\tif (reset_signal = 'U') then \n"
         + "\t\t\t  reset_signal <= '1';\n"
         + "\t\t\t  wait for 100 ns;\n"
         + "\t\t\t  reset_signal <= '0';\n"
         + "\t\t\t  inputs_signal <= (others => '0');\n"
         + "\t\t\telse\n"
         + "\t\t\t  wait until valid_out_signal = '0'\n"
         + "\t\t\t             and valid_out_signal'event;\n\n";

      if (XfvhdlProperties.calcArithmetic == false) {
         code += "\t\t\t  valor1_out := valor2_out;\n"
            + "\t\t\t  valor2_out := conv_integer('0' & "
            + "output_signal);\n\n"
            + "\t\t\t  if (ciclos_off >= 2) then\n";
      } else {
         code += "\t\t\t  valor1_out := conv_integer('0' & "
            + "output_signal);\n\n"
            + "\t\t\t  if (ciclos_off >= 3) then\n";
      }

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\t\t\t\twrite(linea, valor1_in" + i + ", right, 7);\n";
      }

      code += "\t\t\t\twrite(linea, valor1_out, right, 7);\n";

      code += "\t\t\t\twriteline(fichero, linea);\n"
         + "\t\t\t\tif (valor1_in1 = 2**N - 1) then\n"
         + "\t\t\t\t  write(linea, ' ');\n"
         + "\t\t\t\t  writeline(fichero, linea);\n"
         + "\t\t\t\tend if;\n"
         + "\t\t\t  else\n"
         + "\t\t\t\tciclos_off := ciclos_off + 1;\n"
         + "\t\t\t  end if;\n\n";

      if (XfvhdlProperties.calcArithmetic == false) {
         for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
            code += "\t\t\t  valor1_in"
               + i
               + " := valor2_in"
               + i
               + ";\n"
               + "\t\t\t  valor2_in"
               + i
               + " := conv_integer('0' & input"
               + i
               + "_signal);\n";
         }
      } else {
         for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
            code += "\t\t\t  valor1_in"
               + i
               + " := valor2_in"
               + i
               + ";\n"
               + "\t\t\t  valor2_in"
               + i
               + " := valor3_in"
               + i
               + ";\n"
               + "\t\t\t  valor3_in"
               + i
               + " := conv_integer('0' & input"
               + i
               + "_signal);\n";
         }
      }

      code += "\t\t\t  inputs_signal <= inputs_signal + 1;\n"
         + "\n"
         + "\t\t\t  if (";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         if (XfvhdlProperties.entradas == 1)
            code += "valor1_in1 = 2**N - 1";
         else {
            if (i == 1)
               code += "(valor1_in" + i + " = 2**N -1)";
            else
               code += " and (valor1_in" + i + " = 2**N -1)";
         }
      }

      code += ") then\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\t\t\t\twrite(linea, valor1_in" + i + ", right, 7);\n";
      }

      code += "\t\t\t\twrite(linea, valor1_out, right, 7);\n";

      code += "\t\t\t\twriteline(fichero, linea);\n"
         + "\t\t\t\tinputs_signal <= (others => '0');\n"
         + "\t\t\t\treport \"-- END OF SIMULATION --\";\n"
         + "\t\t\t\twait;\t\t-- End of simulation.\n"
         + "\t\t\t  end if;\n"
         + "\t\t\tend if;\n"
         + "\t\tend process;\n"
         + "end FPGA;";

      return code;
   }

}
